#-*- coding:utf-8 -*-
from ea_predict_r import *
import pandas as pd
from prettytable import PrettyTable
# from ea_fd import ea_fd_sensor
# from vno_dot import *
# import ea_fd2

def get_qhp_dict(vnet, dg):
    q_dict = {}
    h_dict = {}
    p_dict = {}
    for ee in dg.es:
        if is_zero_edge(ee):
            continue
        q_dict[ee['id']] = ee['q']
        h_dict[ee['id']] = f0(ee)
    for vv in dg.vs:
        if (vnet.sn != vv.index)  and  (vnet.tn != vv.index):
            p_dict[vv['id']] = vv['p']
    return q_dict,h_dict,p_dict

class VNet:
    # 调整set为词典
    def __adjust_set_dic__(self,datas_set,datas_dic):
        for ds in datas_set:
            datas_dic[ds['id']] = ds

    # 初始化属性数据
    def __init_pro_datas__(self,pro_datas):
        self.node_set = pro_datas['node_set']
        self.edge_set = pro_datas['edge_set']
        self.fan_set = pro_datas['fan_set']
        self.struct_set = pro_datas['struct_set']
        self.sensor_hs_set = pro_datas['sensor_hs_set']
        self.sensor_qs_set = pro_datas['sensor_qs_set']
        node_set = {}
        edge_set = {}
        fan_set = {}
        struct_set = {}
        sensor_hs_set = {}
        sensor_qs_set = {}
        self.__adjust_set_dic__(pro_datas['node_set'],node_set)
        self.__adjust_set_dic__(pro_datas['edge_set'],edge_set)
        self.__adjust_set_dic__(pro_datas['fan_set'],fan_set)
        self.__adjust_set_dic__(pro_datas['struct_set'],struct_set)
        self.__adjust_set_dic__(pro_datas['sensor_hs_set'],sensor_hs_set)
        self.__adjust_set_dic__(pro_datas['sensor_qs_set'],sensor_qs_set)
        self.pro_datas = {'node_set':node_set,'edge_set':edge_set,'fan_set':fan_set,'struct_set':struct_set,'sensor_hs_set':sensor_hs_set,'sensor_qs_set':sensor_qs_set}
    
    # 构造函数
    def __init__(self,pro_datas,graph_datas):
        # self.pro_datas = pro_datas
        # print(self.pro_datas)
        self.__init_pro_datas__(pro_datas)
        # print(self.pro_datas)
        self.graph_datas = graph_datas

    # ****************算法选择，algo参数************
    # ga:遗传算法
    # pso:粒子群
    # dea:差分进化
    # es：进化策略
    # ****************算法选择，algo参数************
    # *****终止条件, terminator*************
    #   1. 按最大进化代数终止迭代
    #   2. 按最大适应值计算次数终止迭代
    #   3. 按种群平均适应值收敛终止迭代
    # *****终止条件, terminator*************
    # *************参数args选择*********************
    # popsize:种群大小
    # cross_rate:交叉概率(0-1，一般0.9，较大)
    # mutation_rate:变异概率(0-1，一般0.015，较小)
    # terminator: 终止条件
    # *************参数args选择*********************
    def run_ea(self,args,use_sensors):
        if use_sensors:
            if args['sel_params'] is None:
                return ea(self.pro_datas,self.graph_datas,args = args)
            return ea_sensor(self.pro_datas,self.graph_datas,args = args)
        return ea(self.pro_datas,self.graph_datas,args = args)

    def run_fault_diag_ea(self,args):
        return ea_fd_sensor(self.graph_datas,args = args)
        # return ea_fd2.test_ea_sensor(self.graph_datas,args = args)

    def save_results(self,out_dir):
        creat_dir(out_dir)
        # 数据保存
        f = open(out_dir+'/property_ret.json', 'w')
        json.dump(self.pro_datas, f, indent=4)
        f.close()

    def run_vno_delta_r(self, dot_dir):
        #构造通风网络,读取并设置相关数据
        vnet = VentNetwork()
        if not build_network(self.graph_datas, vnet.graph()):
            print('\t-->构造通风网络失败!!!')
            return False
        #添加虚拟源汇,将网络变成单一源汇通风网络
        vnet.addVirtualST()
        dg = vnet.graph()
        edges = dg.es
        for index,ee in enumerate(edges):
            if is_zero_edge(ee):
                continue
            for i,pro_e in enumerate(self.edge_set):
                if str(pro_e['id']) == ee['id'] and pro_e['r_adjust_code'] == 1:
                    if abs(pro_e['delta_r']) > 0:
                        ee['r'] += pro_e['delta_r']
                        if ee['r'] < 0:
                            print('分支[%s]风阻为负'%ee['id'])
                            return False
                        print('分支[%s]风阻变化：%.4lf'%(ee['id'],pro_e['delta_r']))
                    break
        if not vno(vnet):
            print('[初始化]通风网络解算失败,程序退出!')
            return False
        print_network(vnet, msg='网络解算结果')
        # return
        # print_mrp_fans(vnet)
        print_mrp(vnet)
        creat_dir(dot_dir)
        # write_vnet_network_to_dotfile(dg,dot_dir+'sample.dot')
        dg = vnet.graph()
        qs,hs,ps = get_qhp_dict(vnet,dg)
        edges = dg.es
        nodes = dg.vs
        for ee in edges:
            if is_zero_edge(ee):
                continue
            for e in self.edge_set:
                if str(e['id']) == ee['id']:
                    e['q']=round(qs[ee['id']],DOUBLE_PRECISION)
                    e['h']=round(hs[ee['id']],DOUBLE_PRECISION)
                    e['r']=round(e['r0'],DOUBLE_PRECISION)+round(e['delta_r'],DOUBLE_PRECISION)
                    break
        for vv in nodes:
            if vnet.sn == vv.index  or  vnet.tn == vv.index:
                continue
            for pro_v in self.node_set:
                if str(pro_v['id']) == vv['id']:
                    pro_v['p']=round(ps[vv['id']],DOUBLE_PRECISION)
                    break
        # 恢复风阻
        for index,ee in enumerate(edges):
            if is_zero_edge(ee):
                continue
            for i,pro_e in enumerate(self.edge_set):
                if str(pro_e['id']) == ee['id'] and pro_e['r_adjust_code'] == 1:
                    ee['r'] = pro_e['r0']
                    break
        # 删除虚拟源汇
        vnet.delVirtualST()
        return True

    def run_vno(self):
        #构造通风网络,读取并设置相关数据
        vnet = VentNetwork()
        if not build_network(self.graph_datas, vnet.graph()):
            print('\t-->构造通风网络失败!!!')
            return False
        #添加虚拟源汇,将网络变成单一源汇通风网络
        vnet.addVirtualST()
        if not vno(vnet):
            print('通风网络解算失败,程序退出!')
            return False
        print_network(vnet, msg='网络解算结果')
        print_mrp_fans(vnet)
        print_mrp(vnet)
        dg = vnet.graph()
        qs,hs,ps = get_qhp_dict(vnet,dg)
        edges = dg.es
        nodes = dg.vs
        for ee in edges:
            if is_zero_edge(ee):
                continue
            for e in self.edge_set:
                if str(e['id']) == ee['id']:
                    e['q']=round(qs[ee['id']],DOUBLE_PRECISION)
                    e['h']=round(hs[ee['id']],DOUBLE_PRECISION)
                    e['r']=round(e['r0'],DOUBLE_PRECISION)
                    break
        for vv in nodes:
            if vnet.sn == vv.index  or  vnet.tn == vv.index:
                continue
            for pro_v in self.node_set:
                if str(pro_v['id']) == vv['id']:
                    pro_v['p']=round(ps[vv['id']],DOUBLE_PRECISION)
                    break
        # 删除虚拟源汇
        vnet.delVirtualST()
        return True

    def source_edges(self):
        #构造通风网络,读取并设置相关数据
        vnet = VentNetwork()
        if not build_network(self.graph_datas, vnet.graph()):
            print('\t-->构造通风网络失败!!!')
            return
        edges = vnet.sourceEdges()
        edge_ids = [ee['id'] for ee in edges]
        return edge_ids

    def sink_edges(self):
        #构造通风网络,读取并设置相关数据
        vnet = VentNetwork()
        if not build_network(self.graph_datas, vnet.graph()):
            print('\t-->构造通风网络失败!!!')
            return
        edges = vnet.sinkEdges()
        edge_ids = [ee['id'] for ee in edges]
        return edge_ids

    # 单向回路
    def unidir_circle(self):
        #构造通风网络,读取并设置相关数据
        vnet = VentNetwork()
        if not build_network(self.graph_datas, vnet.graph()):
            print('\t-->构造通风网络失败!!!')
            return
        comps = vnet.SCC()
        return comps
        
    def get_st_ids(self,edge_id):
        for eds in self.edge_set:
            if eds['id'] == str(edge_id):
                return [eds['s'],eds['t']]
        return None

    def print_tople(self,save_dir = None):
        print(u"********拓扑关系********")
        vnet = VentNetwork()
        if not build_network(self.graph_datas, vnet.graph()):
            print('\t-->构造通风网络失败!!!')
            return
        col_name = [u"节点", u"入边", u"出边", u"结果"]
        x = PrettyTable(col_name, encoding = "gbk")
        x.sortby = u"节点"
        x.align = "l"
        dg = vnet.graph()
        datas = []
        for v in dg.vs:
            in_edges = [dg.es[i]['id'] for i in dg.incident(v, mode=IN)]
            out_edges = [dg.es[i]['id'] for i in dg.incident(v, mode=OUT)]
            in_str = '无'
            out_str = '无'
            ret_str = ""
            if len(in_edges) > 0:
                in_str = 'e'+',e'.join(in_edges)
                ret_str = 'e'+'-e'.join(in_edges) + '--' + 'v%s'%v['id']
            else:
                ret_str = 'v%s'%v['id']
            if len(out_edges) > 0:
                out_str = 'e'+',e'.join(out_edges)
                ret_str += '--' + 'e'+'-e'.join(out_edges)
            data = ['v%s'%v['id'],in_str,out_str,ret_str]
            x.add_row(data)
            datas.append(data)
        print(x)
        if save_dir is not None:
            node_frame = pd.DataFrame(datas,columns = col_name)
            creat_dir(save_dir)
            print('写入拓扑数据...')
            node_frame.to_csv(save_dir+'tople_ret.csv', index =False, encoding = "gbk")

    # 负风量分支
    def print_negative_q(self):
        x = PrettyTable([u"分支", u"始节点", u"末节点", "r0", "r","delta_r","sensor_q","sensor_h","q","h"], encoding = "gbk")
        edge_num = 0
        for eds in self.edge_set:
            if eds['q'] < 0:
                edge_num += 1
                x.add_row([eds['name'],'v%d'%eds['s'],'v%d'%eds['t'],eds['r0'],eds['r'],eds['delta_r'],eds['sensor_q'],eds['sensor_h'],eds['q'],eds['h']])
        if edge_num > 0:
            print(u"********负风量分支数据********")
            print(x)
            return
        print('********无负风量分支********')

    # 固定风量分支
    def print_fix_q(self):
        x = PrettyTable([u"分支", u"始节点", u"末节点", u"风阻", u"固定风量"], encoding = "gbk")
        edge_num = 0
        for eds in self.edge_set:
            if eds['need_q'] > 0:
                edge_num += 1
                x.add_row([eds['name'],'v%d'%eds['s'],'v%d'%eds['t'],eds['r0'],eds['need_q']])
        if edge_num > 0:
            print(u"********固定风量分支数据********")
            print(x)
            return
        print('********无固定风量分支********')

        
    def print_edge_datas_by_id(self,edge_ids, msg = ''):
        x = PrettyTable([u"分支", u"始节点", u"末节点", u"初始风阻", u"解算风阻",u"调节风阻",u"风量传感器",u"压差传感器",u"备注"], encoding = "gbk")
        # x.sortby = u"分支"
        # x.reversesort = False
        # x.int_format = "04"
        # x.float_format = ".4"
        # x.align[u"分支"] = "l" # Left align city names
        
        print(u"********%s********"%msg)
        for eds in self.edge_set:
            for _id in edge_ids:
                if str(eds['id']) != _id:
                    continue
                x.add_row([eds['id'],'v%d'%eds['s'],'v%d'%eds['t'],eds['r0'],eds['r'],eds['delta_r'],eds['sensor_q'],eds['sensor_h'],eds['note']])
        print(x)

    def print_test(self):
        edgedf = pd.DataFrame(self.edge_set)
        print(edgedf)

    # 打印节点数据
    def print_node_datas(self,save_dir = None,encoding='utf-8'):
        print(u"********节点数据********")
        col_name = [u"节点", u"压能"]
        x = PrettyTable(col_name, encoding = "gbk")
        datas = []
        x.float_format = ".4"
        for nds in self.node_set:
            data = [nds['id'],nds['p']]
            x.add_row(data)
            datas.append(data)
        print(x)
        if save_dir is not None:
            node_frame = pd.DataFrame(datas,columns = col_name)
            tmp_dir = save_dir + '/results/'
            creat_dir(tmp_dir)
            node_frame.to_csv(tmp_dir+'node_ret.csv', index =False, encoding = 'gb2312')
            # node_frame.drop([u"压能传感器",u"备注"],axis=1,inplace=True)
            node_frame.set_axis(['id', 'p'], axis='columns',inplace=True)
            creat_dir(save_dir)
            print('写入节点数据...')
            node_frame.to_csv(save_dir+'node_set_ret.csv', index =False, encoding = encoding)

    # 打印分支数据
    def print_edge_datas(self,save_dir = None,encoding='utf-8'):
        print(u"********分支数据********")
        col_name = [u"分支", u"始节点", u"末节点", u"初始风阻", u"解算风阻",u"调节风阻",u"风量",u"压力", u"备注"]
        x = PrettyTable(col_name, encoding = "gb2312")
        datas = []
        for eds in self.edge_set:
            data = [eds['id'],eds['s'],eds['t'],eds['r0'],eds['r'],eds['delta_r'],eds['q'],eds['h'],eds['备注']]
            x.add_row(data)
            datas.append(data)
        print(x)
        if save_dir is not None:
            edge_frame = pd.DataFrame(datas,columns = col_name)
            tmp_dir = save_dir + '/results/'
            creat_dir(tmp_dir)
            # edge_frame.to_csv(tmp_dir+'edge_ret.csv', index =False, encoding = 'gb2312')
            # edge_frame.drop([u"风量传感器",u"压差传感器",u"固定风量", u"备注"],axis=1,inplace=True)
            # h_vector = [abs(hv) for hv in edge_frame[u"压力"]]
            # edge_frame.insert(8,'h_vector',h_vector,allow_duplicates=True)
            # edge_frame.set_axis(['id', 's', 't', 'r0', 'r', 'delta_r', 'q', 'h','备注'], axis='columns',inplace=True)
            # creat_dir(save_dir)
            print('写入分支数据...')
            edge_frame.to_csv(tmp_dir+'edges_ret.csv', index =False, encoding = 'gb2312')

    # 打印构筑物数据
    def print_struct_datas(self,save_dir = None,encoding='utf-8'):
        print(u"********构筑物数据********")
        col_name = ['id', u"分支", u"初始风阻", u"风阻",u"调节风阻",u"风量",u"压力"]
        x = PrettyTable(col_name, encoding = "gb2312")
        datas = []
        for st in self.struct_set:
            if 'h' in st:
                data = [st['id'],st['e_id'],st['r0'],st['r'],st['delta_r'],st['q'],st['h']]
                x.add_row(data)
                datas.append(data)
        print(x)
        if save_dir is not None:
            st_df = pd.DataFrame(datas,columns = col_name)
            tmp_dir = save_dir + '/results/'
            creat_dir(tmp_dir)
            print('写入构筑物数据...')
            st_df.to_csv(tmp_dir+'structs_ret.csv', index =False, encoding = 'gb2312')

    # 打印压差数据
    def print_delta_h_datas(self,save_dir = None):
        col_name = [u"id",u"始节点", u"末节点", u"压差传感器",'计算压差']
        x = PrettyTable(col_name, encoding = "gbk")
        datas = []
        for dh in self.sensor_hs_set:                
            # print(dh)
            if 'h' not in dh:
                continue
            data = [dh['id'],dh['s'],dh['t'],dh['h']]
            if 'delta_h' in dh:
                data.append(dh['delta_h'])
            else:
                data.append('')
            x.add_row(data)
            datas.append(data)
        if len(datas) < 1:
            return
        print(u"********压差数据********")
        print(x)
        if save_dir is not None:
            tmp_dir = save_dir + '/results/'
            creat_dir(tmp_dir)
            hs_df = pd.DataFrame(datas,columns = col_name)
            print('写入分支数据...')
            hs_df.to_csv(tmp_dir+'sensors_hs_ret.csv', index =False, encoding = "gb2312")
    
    # 打印风机数据
    def print_fan_datas(self):
        print(u"********风机数据********")
        x = PrettyTable([u"风机", u"所在分支", "a0", "a1","a2"], encoding = "gbk")
        print(u"id\t分支id\ta0\ta1\ta2")
        for fds in self.fan_set:
            x.add_row(['fan%d'%eds['id'],'e%d'%eds['e_id'],fds['a0'],fds['a1'],fds['a2']])
        print(x)

    # 打印风量传感器数据
    def print_delta_q_datas(self,save_dir = None):
        col_name = [u"分支", u"风量传感器",'计算风量']
        x = PrettyTable(col_name, encoding = "gbk")
        datas = []
        for dq in self.sensor_qs_set:                
            # print(dh)
            if 'q' not in dq:
                continue
            data = [dq['e_id'],dq['q']]
            if 'cacul_q' in dq:
                data.append(dq['cacul_q'])
            else:
                data.append('')
            x.add_row(data)
            datas.append(data)
        if len(datas) < 1:
            return
        print(u"********风量传感器数据********")
        print(x)
        if save_dir is not None:
            tmp_dir = save_dir + '/results/'
            creat_dir(tmp_dir)
            qs_df = pd.DataFrame(datas,columns = col_name)
            print('写入分支数据...')
            qs_df.to_csv(tmp_dir+'sensors_qs_ret.csv', index =False, encoding = "gb2312")

    def print_sensor_datas(self, selected_params):
        if len(selected_params['q']) > 0:
            print(u"********风量传感器数据********")
            x = PrettyTable([u"分支", u"风量"], encoding = "gbk")
            # 风量传感器
            qs = selected_params['q']
            for _id in qs:
                x.add_row([_id,qs[_id]])
            print(x)
    
        # if len(selected_params['delta_h']) > 0:
        #     print(u"********风压传感器数据********")
        #     x = PrettyTable(['始节点', '末节点', u"风压"], encoding = "gbk")
        #     # 风压传感器
        #     hs = selected_params['delta_h']
        #     for _id in hs:
        #         x.add_row([_id.split('-')[0],_id.split('-')[-1],hs[_id]])
        #     print(x)
